package org.jeecg.modules.cosmetic.handler;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.cosmetic.entity.TPackageCalculation;
import org.jeecg.modules.cosmetic.entity.TPackageProduct;
import org.jeecg.modules.cosmetic.model.dto.PackCalculationDTO;
import org.jeecg.modules.cosmetic.model.dto.PackageTypeChildrenDTO;
import org.jeecg.modules.cosmetic.model.dto.PackageTypeDTO;
import org.jeecg.modules.cosmetic.model.dto.PackageTypeMapDTO;
import org.jeecg.modules.cosmetic.model.vo.PackageCalculationVO;
import org.jeecg.modules.cosmetic.model.vo.PackageCategoryResultVO;
import org.jeecg.modules.cosmetic.service.ITPackageCalculationService;
import org.jeecg.modules.cosmetic.service.ITPackageProductService;
import org.jeecg.modules.cosmetic.service.ITPackageTypeService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;



@Service
@Slf4j
public class PackageCalculationHandler {

    @Resource
    private ITPackageTypeService tPackageTypeService;

    @Resource
    private ITPackageCalculationService tPackageCalculationService;

    @Resource
    private ITPackageProductService tPackageProductService;

    /**
     * 查询出产品类别数据
     * @return
     */
    public List<PackageTypeDTO> queryPackageType() {
        return tPackageTypeService.queryPackageType();
    }

    /**
     * 查询出产品类别数据,树型结构
     * @return
     */
    public List<PackageTypeChildrenDTO> queryPackageTypeMap() {
        List<PackageTypeDTO> packageTypeList = tPackageTypeService.queryPackageType();
        Map<String, List<PackageTypeMapDTO>> map = new HashMap<>();
        if (CollectionUtil.isNotEmpty(packageTypeList)) {
            List<PackageTypeMapDTO> list = Lists.newArrayList();
            for (PackageTypeDTO packageTypeDTO : packageTypeList) {
                for (PackageTypeDTO typeDTO : packageTypeList) {
                    if (packageTypeDTO.getUnits().equals(typeDTO.getUnits())) {
                        PackageTypeMapDTO typeMapDTO = new PackageTypeMapDTO();
                        typeMapDTO.setCategory(typeDTO.getCategory());
                        typeMapDTO.setCategoryKeys(typeDTO.getCategoryKeys());
                        list.add(typeMapDTO);
                    }
                }
                map.put(packageTypeDTO.getUnits(), list);
                list = Lists.newArrayList();
            }
        }

        List<PackageTypeChildrenDTO> typeChildrenDTOS = Lists.newArrayList();
        AtomicInteger i = new AtomicInteger();
        map.forEach((key, value) -> {
            AtomicInteger j = new AtomicInteger();
            List<PackageTypeChildrenDTO.Children> childrenList = Lists.newArrayList();
            PackageTypeChildrenDTO packageTypeChildrenDTO = new PackageTypeChildrenDTO();
            value.forEach(items -> {
                PackageTypeChildrenDTO.Children children = new PackageTypeChildrenDTO.Children();
                children.setLabel(items.getCategory());
                children.setValue(j.getAndIncrement());
                children.setCategory(items.getCategory());
                children.setCategoryKeys(items.getCategoryKeys());
                childrenList.add(children);
                packageTypeChildrenDTO.setChildren(childrenList);
            });
            packageTypeChildrenDTO.setValue(i.getAndIncrement());
            packageTypeChildrenDTO.setLabel(key);
            typeChildrenDTOS.add(packageTypeChildrenDTO);
        });
        return typeChildrenDTOS;
    }

    /**
     * 提交计算方法
     * @param vo
     * @param list
     * @return
     */
    @Transactional
    @DS("cosmetic-datasource")
    public Result<PackCalculationDTO> submitPackCalculation(PackageCalculationVO vo, List<PackageCategoryResultVO> list) {
        // 判断 List 是否为空
        if (CollectionUtil.isEmpty(list)) {
            log.error("产品类别数据为空");
            return Result.error("产品类别数据为空");
        }
        
        // 判断商品输入的是体积还是长宽高,计算出对应的体积
        String product = vo.getProduct();
        BigDecimal packageVolume;
        BigDecimal constant = new BigDecimal("1000");
        if (StringUtils.isBlank(vo.getVolume())) {
            BigDecimal packageLongs = new BigDecimal(vo.getLongs());
            BigDecimal packageWidth = new BigDecimal(vo.getWidth());
            BigDecimal packageHigh = new BigDecimal(vo.getHigh());
            BigDecimal packageVolumeCM = packageLongs.multiply(packageWidth).multiply(packageHigh);
            packageVolume = packageVolumeCM.multiply(constant);
            vo.setVolume(packageVolumeCM.toString());
        } else {
            packageVolume = new BigDecimal(vo.getVolume()).multiply(constant);
        }
        
        // 先保存主表数据
        TPackageCalculation tPackageCalculation = new TPackageCalculation();
        BeanUtils.copyProperties(vo, tPackageCalculation);
        tPackageCalculationService.save(tPackageCalculation);
        
        // 根据商品类型选择商品
        PackCalculationDTO packCalculationDTO = new PackCalculationDTO();
        packCalculationDTO.setProductType(product);
        BigDecimal addVolume = BigDecimal.ZERO;
        BigDecimal addInput = BigDecimal.ZERO;
        BigDecimal maxInput = BigDecimal.ZERO;
        BigDecimal k;
        for (PackageCategoryResultVO resultDTO : list) {
            BigDecimal key;
            BigDecimal input = new BigDecimal(resultDTO.getInputContent());
            if (product.equals("0") && input.compareTo(new BigDecimal("0.2")) < 0) {
                key = new BigDecimal("80.0");
            } else {
                key = new BigDecimal(resultDTO.getCategoryKeys());
            }
            BigDecimal calVolume = input.multiply(constant).multiply(key);
            
            // 保存子表数据
            TPackageProduct tPackageProduct = new TPackageProduct();
            BeanUtils.copyProperties(resultDTO, tPackageProduct);
            tPackageProductService.save(tPackageProduct);
            
            // 单品
            if (product.equals("0")) {
                k = packageVolume.subtract(calVolume).divide(packageVolume, 2, BigDecimal.ROUND_UP);
                if (k.compareTo(BigDecimal.ZERO) < 0) {
                    return Result.error("包装空隙率小于0");
                }
                return Result.OK(selectCalculationResult(packCalculationDTO, input, calVolume, k, tPackageCalculation));
            }

            // 套装[单独使用]
            if (product.equals("1")) {
                addVolume = addVolume.add(calVolume);
                if (input.compareTo(maxInput) >= 0) {
                    maxInput = input;
                }
            }

            // 套装[混合使用]
            if (product.equals("2")) {
                addVolume = addVolume.add(calVolume);
                addInput = addInput.add(input);
            }
        }
        k = packageVolume.subtract(addVolume).divide(packageVolume, 2, BigDecimal.ROUND_UP);
        if (k.compareTo(BigDecimal.ZERO) < 0) {
            return Result.error("包装空隙率小于0");
        }
        if (product.equals("1")) {
            return Result.OK(selectCalculationResult(packCalculationDTO, maxInput, addVolume, k,tPackageCalculation));
        }
        if (product.equals("2")) {
            return Result.OK(selectCalculationResult(packCalculationDTO, addInput, addVolume, k, tPackageCalculation));
        }
        return Result.OK(packCalculationDTO);
    }

    /**
     * 根据标准空隙率比较计算后的结果
     * @param packCalculationDTO
     * @param input
     * @param calVolume
     * @param k
     * @return
     */
    private PackCalculationDTO selectCalculationResult(PackCalculationDTO packCalculationDTO, BigDecimal input, BigDecimal calVolume, BigDecimal k, TPackageCalculation tPackageCalculation) {
        compareLeftAndRight(packCalculationDTO, input, calVolume, k, tPackageCalculation, null, "1", "0.85", "0.15");
        compareLeftAndRight(packCalculationDTO, input, calVolume, k, tPackageCalculation, "1", "5", "0.70", "0.30");
        compareLeftAndRight(packCalculationDTO, input, calVolume, k, tPackageCalculation, "5", "15", "0.60", "0.40");
        compareLeftAndRight(packCalculationDTO, input, calVolume, k, tPackageCalculation, "15", "30", "0.50", "0.50");
        compareLeftAndRight(packCalculationDTO, input, calVolume, k, tPackageCalculation, "30", "50", "0.40", "0.60");
        compareLeftAndRight(packCalculationDTO, input, calVolume, k, tPackageCalculation, "50", null, "0.30", "0.70");

        // 更新主表数据
        tPackageCalculation.setResultContent(k.toString());
        tPackageCalculationService.updateById(tPackageCalculation);

        // 更新子表数据
        List<TPackageProduct> productList = tPackageProductService.list(new LambdaQueryWrapper<TPackageProduct>().isNull(TPackageProduct::getCalId));
        if (CollectionUtil.isNotEmpty(productList)) {
            productList.forEach(i -> {
                i.setCalId(tPackageCalculation.getId());
            });
            tPackageProductService.updateBatchById(productList);
        }
        return packCalculationDTO;
    }

    /**
     * 选定比较区间
     * @param packCalculationDTO
     * @param input
     * @param calVolume
     * @param k
     * @param tPackageCalculation
     * @param left
     * @param right
     * @param norm
     * @param notNorm
     */
    private void compareLeftAndRight(PackCalculationDTO packCalculationDTO, BigDecimal input, BigDecimal calVolume, BigDecimal k, TPackageCalculation tPackageCalculation, String left, String right, String norm, String notNorm) {
        if (StringUtils.isBlank(left)) {
            if (input.compareTo(new BigDecimal(right)) <= 0) {
                setPackResult(packCalculationDTO, input, calVolume, k, tPackageCalculation, norm, notNorm);
            }
        } else if (StringUtils.isBlank(right)) {
            if (input.compareTo(new BigDecimal(left)) > 0) {
                setPackResult(packCalculationDTO, input, calVolume, k, tPackageCalculation, norm, notNorm);
            }

        } else if ((input.compareTo(new BigDecimal(left)) > 0) && (input.compareTo(new BigDecimal(right))) <= 0) {
            setPackResult(packCalculationDTO, input, calVolume, k, tPackageCalculation, norm, notNorm);
        }
    }

    /**
     * 设置比较结果
     * @param packCalculationDTO
     * @param input
     * @param calVolume
     * @param k
     * @param tPackageCalculation
     * @param norm
     * @param notNorm
     */
    private void setPackResult(PackCalculationDTO packCalculationDTO, BigDecimal input, BigDecimal calVolume, BigDecimal k, TPackageCalculation tPackageCalculation, String norm, String notNorm) {
        if ((k.compareTo(BigDecimal.ZERO) >0) && (k.compareTo(new BigDecimal(norm))) <= 0) {
            packCalculationDTO.setQualified(true);
            tPackageCalculation.setResultStatus(0);
            tPackageCalculation.setResult("合格");
        } else {
            setPackCalculationVO(packCalculationDTO, input, calVolume, notNorm, tPackageCalculation);
        }
    }

    /**
     * 设置返回类方法
     * @param packCalculationDTO
     * @param input
     * @param calVolume
     * @param reverseKey
     */
    private void setPackCalculationVO(PackCalculationDTO packCalculationDTO, BigDecimal input, BigDecimal calVolume, String reverseKey, TPackageCalculation tPackageCalculation) {
        BigDecimal packageGap = calVolume.divide(new BigDecimal("1000"), 2, BigDecimal.ROUND_UP).divide(new BigDecimal(reverseKey), 2, BigDecimal.ROUND_UP);
        packCalculationDTO.setQualified(false);
        packCalculationDTO.setInputContent(input.toString());
        packCalculationDTO.setRangeVolume(packageGap.toString());

        tPackageCalculation.setResultVolume(packageGap.toString());
        tPackageCalculation.setResultStatus(-1);
        tPackageCalculation.setResult("不合格");
    }
}
